﻿#include "Graphics.h"

// allows for easy debugging

Graphics::Graphics()
	: m_pD3DDevice(NULL)
	, m_hWnd(NULL)
	, m_bWindowed(true)
	, m_bIsOriented(false)
	, m_fScreenWidth(800)
	, m_fScreenHeight(600)
{
	// initialize pointers to NULL
	for(int i = 0; i < g_numMeshes; i++)
	{
		m_pMeshLibrary[i] = NULL;
		m_tpTexLibrary[i].lpTexture = NULL;
	}

	// setup the camera
	m_Camera.eyePos = D3DXVECTOR3(400, 300, -10);
	m_Camera.lookAt = D3DXVECTOR3(0, 0, 0);
	m_Camera.upVec  = D3DXVECTOR3(0, 1, 0);
}
bool Graphics::SetupDevice(HWND hWnd)
{
	IDirect3D9* pD3DObject = NULL;
	pD3DObject = Direct3DCreate9(D3D_SDK_VERSION);			// create the d3dobject

	m_hWnd = hWnd;											// make a copy of hWnd for Graphics

	RECT rClientRect;										// get screen dimensions
	::GetClientRect(hWnd, &rClientRect);

	m_fScreenWidth = (float)(rClientRect.right - rClientRect.left);
	m_fScreenHeight = (float)(rClientRect.bottom - rClientRect.top);

	D3DCAPS9 D3DCaps;
	HR(pD3DObject->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &D3DCaps));


	// determine what type of vertex processing to use
	DWORD dwBehaviorFlags = 0;

	if(D3DCaps.VertexProcessingCaps != 0)	
	{dwBehaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING;}
	else	{dwBehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;}	

	//set up present parameters
	m_D3Dpp.hDeviceWindow			= m_hWnd;
	m_D3Dpp.Windowed				= m_bWindowed;
	m_D3Dpp.AutoDepthStencilFormat	= D3DFMT_D24S8;
	m_D3Dpp.EnableAutoDepthStencil	= TRUE;
	m_D3Dpp.BackBufferCount			= 1;
	m_D3Dpp.BackBufferFormat		= D3DFMT_X8R8G8B8;
	m_D3Dpp.BackBufferWidth			= (int)m_fScreenWidth;
	m_D3Dpp.BackBufferHeight		= (int)m_fScreenHeight;
	m_D3Dpp.SwapEffect				= D3DSWAPEFFECT_DISCARD;
	m_D3Dpp.PresentationInterval	= D3DPRESENT_INTERVAL_DEFAULT;
	m_D3Dpp.Flags					= D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;
	m_D3Dpp.MultiSampleQuality		= 0;
	m_D3Dpp.MultiSampleType			= D3DMULTISAMPLE_NONE;

	// setup the d3ddevice using the d3dobject and present parameters
	pD3DObject->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, 
		dwBehaviorFlags, &m_D3Dpp, &m_pD3DDevice);

	if(m_pD3DDevice == NULL)
		return false;

	pD3DObject->Release();
	return true;
}
bool Graphics::SetupFont()
{
	/*INIT FONT*/

	//Top Left
	m_rLeftInst.left	= m_fScreenWidth*0.01f;
	m_rLeftInst.right	= m_rLeftInst.left + 100;
	m_rLeftInst.top		= m_fScreenHeight*0.01f;
	m_rLeftInst.bottom	= m_rLeftInst.top + 50;

	//Top Right
	m_rRighInst.right	= m_fScreenWidth*0.95f;	
	m_rRighInst.left	= m_rRighInst.right - 100;
	m_rRighInst.top		= m_fScreenHeight*0.01f;
	m_rRighInst.bottom	= m_rRighInst.top + 50;

	//Bottom Left
	m_rCameraCrd.left	= m_rLeftInst.left;
	m_rCameraCrd.right	= m_rLeftInst.right;
	m_rCameraCrd.bottom	= m_fScreenHeight*0.95f;
	m_rCameraCrd.top	= m_rCameraCrd.bottom - 50;

	//Bottom Right
	m_rPlayerCrd.left	= m_rRighInst.left;
	m_rPlayerCrd.right	= m_rRighInst.right;
	m_rPlayerCrd.bottom	= m_rCameraCrd.bottom;
	m_rPlayerCrd.top	= m_rCameraCrd.top;

	//Mid Left
	m_rCreditCrd.left	= m_rLeftInst.left;	
	m_rCreditCrd.right	= m_rLeftInst.right;
	m_rCreditCrd.top	= m_fScreenHeight*0.5f;
	m_rCreditCrd.bottom	= m_rCreditCrd.top + 50;


	D3DXCreateFont(m_pD3DDevice, 18, 0, NULL, 0, false,		//Create font object
		DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY,
		DEFAULT_PITCH | FF_DONTCARE, TEXT("Times New Roman"),
		&m_pD3DFont);

	if(m_pD3DFont == NULL)
		return false;

	m_Printer << "Initializing...";

	return true;
}
bool Graphics::SetupMeshes()
{
	// all objects have the ball mesh as a placeholder
	m_cszMeshNames[BRICK] = "brick.X";
	m_cszMeshNames[PLAYER] = "Ball.X";
	m_cszMeshNames[ENEMY] = "Ball.X";

	// attempt to load meshes
	for(int i = 0; i < g_numMeshes; i++)
	{
		LoadMeshFile(m_cszMeshNames[i], m_pMeshLibrary[i]);
		if(m_pMeshLibrary[i] == NULL)	// if any mesh could not be loaded, stop the program
			return false;
	}

	return true;						// return true if everything went well

}
bool Graphics::SetupTextures()
{
	// store location of texture files in the texture file library of Graphics singleton
	m_cszTexNames[BRICK] = "Brick.jpg";
	m_cszTexNames[PLAYER] = "happy-face.png";
	m_cszTexNames[ENEMY] = "happy-face.png";



	// attempt to load textures from the above files
	for(int i = 0; i < g_numTextures; i++)
	{
		D3DXCreateTextureFromFileEx(m_pD3DDevice, m_cszTexNames[i], 0, 0, 0, 0, 
			D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT,
			D3DX_DEFAULT, D3DCOLOR_XRGB(255, 0, 255),
			&m_tpTexLibrary[i].imgInfo, 0, &m_tpTexLibrary[i].lpTexture);	// results stored in m_tpTexLibrary[]

		if(m_tpTexLibrary[i].lpTexture == NULL)		// if any file was unable to be loaded, stop the program
			return false;
	}

	return true;
}
bool Graphics::SetupLight()		// setup a very basic D3DLIGHT9 light
{

	m_pD3DDevice->SetRenderState(D3DRS_SPECULARENABLE, TRUE);
	m_pD3DDevice->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(60, 60, 60));

	ZeroMemory(&m_DefaultLight, sizeof(D3DLIGHT9));

	m_DefaultLight.Type = D3DLIGHT_POINT;
	m_DefaultLight.Ambient = D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.0f);
	m_DefaultLight.Diffuse = D3DXCOLOR(0, 0, 1, 1);	// color of the light
	m_DefaultLight.Specular = D3DXCOLOR(1, 0, 1, 1);
	m_DefaultLight.Position.x = 30;	
	m_DefaultLight.Position.y = 10;	
	m_DefaultLight.Position.z = -10;
	D3DXVec3Normalize((D3DXVECTOR3*)&m_DefaultLight.Direction, (D3DXVECTOR3*)&m_DefaultLight.Position);
	m_DefaultLight.Range = 500.0f;
	m_DefaultLight.Attenuation0 = 0;
	m_DefaultLight.Attenuation1 = 0.05f;
	m_DefaultLight.Attenuation2 = 0.0f;

	m_pD3DDevice->SetLight(0, &m_DefaultLight);
	m_pD3DDevice->LightEnable(0, true);

	//TODO: Validate that Light was set correctly
	return true;
}
bool Graphics::Init(HWND hWnd)		// returns false if unsuccessful
{
	// run through each part of setup, stopping if any of them failed
	if(!SetupDevice(hWnd))	
		return false;
	else if(!SetupFont())	
		return false;
	else if(!SetupMeshes())	
		return false;
	else if(!SetupTextures())
		return false;
	else if(!SetupLight())
		return false;

	// setup a basic material to use and set it to the device
	ZeroMemory(&m_dmDefaultMaterial, sizeof(D3DMATERIAL9));
	m_dmDefaultMaterial.Ambient = D3DXCOLOR(0, 0, 0, 1);
	m_dmDefaultMaterial.Diffuse = D3DXCOLOR(0, 1, 0, 1);
	m_dmDefaultMaterial.Specular = D3DXCOLOR(1, .5f, 0, 1);
	m_dmDefaultMaterial.Power = 20;

	m_pD3DDevice->SetRenderState(D3DRS_SPECULARMATERIALSOURCE, D3DMCS_MATERIAL);

	D3DXMatrixPerspectiveFovLH(&m_matPerspective, D3DXToRadian(65), 
		m_fScreenWidth / m_fScreenHeight, 1.0f, 1000.0f);
	m_pD3DDevice->SetTransform(D3DTS_PROJECTION, &m_matPerspective);

	return true;
}
void Graphics::CheckDevice()
{
	// Watch for our window being covered, minimized or otherwise deactivated
	if (NULL == m_pD3DDevice){return;}							//If we have lost our D3DDevice pointer, just return
	HRESULT hDevState = m_pD3DDevice->TestCooperativeLevel();	// Check for lost device 
	switch (hDevState)
	{
	case D3DERR_DEVICELOST:		
		//If the device is lost return
		//check in the next frame
		::OutputDebugString("Device lost"); 		
		::Sleep(100);		
		return;	

	case D3DERR_DEVICENOTRESET:
		if (FAILED(m_pD3DDevice->Reset(&m_D3Dpp)))
		{
			::OutputDebugString("Reset failed");
			return;
		}
		break;
	}// Done checking for deactivation and lost devices; all is well
}
bool Graphics::Render(DrawList& drawlist)
{
	D3DXVECTOR3 playerPos;
	m_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DXCOLOR(0.01f, 0.1f, 0.1f, 0.1f), 1.0f, 0);

	// set the camera view
	D3DXMatrixLookAtLH(&m_matView,
		&m_Camera.eyePos, &m_Camera.lookAt, &m_Camera.upVec);
	m_pD3DDevice->SetTransform(D3DTS_VIEW, &m_matView);

	HRESULT hr = m_pD3DDevice->BeginScene();
	if(FAILED(hr))
		return false;

	for each(BaseObject* pObj in drawlist)
	{
		// prepare matrices that the world matrix for each object shall be composed of

		D3DXMatrixScaling(&m_matScale, pObj->m_fScale,
			pObj->m_fScale, 
			pObj->m_fScale);

		D3DXMatrixRotationYawPitchRoll(&m_matRot, pObj->m_stRotation.yaw, 
			pObj->m_stRotation.pitch,
			pObj->m_stRotation.roll);

		D3DXMatrixTranslation(&m_matTrans,
			pObj->m_vPosition.x,
			pObj->m_vPosition.y,
			pObj->m_vPosition.z);

		// multiply the above three to create the world matrix
		D3DXMatrixMultiply(&m_matWorld, &m_matScale, &m_matRot);
		D3DXMatrixMultiply(&m_matWorld, &m_matWorld, &m_matTrans);

		m_pD3DDevice->SetTransform(D3DTS_WORLD, &m_matWorld);
		if(pObj->m_pTexture == NULL)
		{
			MessageBox(0, "Pointer to texture is trying to be accessed, but is pointing to NULL. Exiting program...",
				0, 0);
			return false;
		}
		if(pObj->m_pMesh == NULL)
		{
			MessageBox(0, "Pointer to mesh is trying to be accessed, but is pointing to NULL. Exiting program...",
				0, 0);
			return false;
		}
		m_pD3DDevice->SetMaterial(&m_dmDefaultMaterial);
		m_pD3DDevice->SetTexture(0, pObj->m_pTexture);
		m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
		pObj->m_pMesh->DrawSubset(pObj->m_dwSubset);

		if(pObj->m_iMyType == PLAYER_TYPE)	// have the light and camera follow the player object
		{
			playerPos = pObj->m_vPosition;
			SetLightPos(playerPos);
			SetCameraFocus(playerPos);
		}
	}

	RenderFont(playerPos);					// display the font to the screen
	hr = m_pD3DDevice->EndScene();
	if(FAILED(hr))
		return false;

	m_pD3DDevice->Present(NULL, NULL, NULL, NULL);

	return true;
}
void Graphics::SetCameraFocus(D3DXVECTOR3 pos)
{
	m_Camera.lookAt.x = pos.x;			// focus on the player on the x and z axes
	m_Camera.lookAt.z = pos.z;

	m_Camera.eyePos.x = pos.x;			// follow the player left and right


	D3DXMatrixLookAtLH(&m_matView,
		&m_Camera.eyePos, &m_Camera.lookAt, &m_Camera.upVec);
	m_pD3DDevice->SetTransform(D3DTS_VIEW, &m_matView);
}

void Graphics::SetCameraPos(D3DXVECTOR3 pos)
{
	m_Camera.eyePos.y = pos.y + 200.0f;		// set the camera position to be a higher up than the actual maze

	// offset the camera to be above and at the edge of the map
	m_Camera.eyePos.x = -10;
	m_Camera.eyePos.z = -50;
}
void Graphics::SetLightPos(D3DXVECTOR3 pos)
{
	m_DefaultLight.Position = pos;
	m_DefaultLight.Position.y += 20;
	m_pD3DDevice->SetLight(0, &m_DefaultLight);
}
void Graphics::Update()
{
	// allows WASD keys to move the camera

	if(DirectInput::Engine()->keyDown(DIK_W))
	{
		if(DirectInput::Engine()->mouseButtonDown(LEFT_MOUSE_BUTTON))
			m_Camera.lookAt.y -= 1.0f;
		else
			m_Camera.eyePos.y -= 1.0f;
	}
	if(DirectInput::Engine()->keyDown(DIK_S))
	{
		if(DirectInput::Engine()->mouseButtonDown(LEFT_MOUSE_BUTTON))
			m_Camera.lookAt.y += 1.0f;
		else
			m_Camera.eyePos.y += 1.0f;
	}
	if(DirectInput::Engine()->keyDown(DIK_A))
	{
		if(DirectInput::Engine()->mouseButtonDown(LEFT_MOUSE_BUTTON))
			m_Camera.lookAt.x -= 1.0f;
		else
			m_Camera.eyePos.x -= 1.0f;
	}
	if(DirectInput::Engine()->keyDown(DIK_D))
	{
		if(DirectInput::Engine()->mouseButtonDown(LEFT_MOUSE_BUTTON))
			m_Camera.lookAt.x += 1.0f;
		else
			m_Camera.eyePos.x += 1.0f;
	}



}
void Graphics::LoadMeshFile(const char* cszXFile, ID3DXMesh*& pMesh)
{

	///* Empty the mesh if it is not empty */

	if (NULL != pMesh)
	{
		pMesh->Release();
	}
	pMesh = NULL;

	/* Prepare buffers returned by D3DXLoadMeshFromX */
	ID3DXBuffer* pAdjacency = NULL;			// required for OptimizeInPlace
	ID3DXBuffer* pMaterials = NULL;			// materials for each subset
	ID3DXBuffer* pEffectsInstances = NULL;	// not interested in effects now
	DWORD dwNumMaterials = 0;

	/* Load the file into the mesh */
	HR(D3DXLoadMeshFromX(cszXFile, D3DXMESH_MANAGED, m_pD3DDevice, &pAdjacency, &pMaterials, &pEffectsInstances, &dwNumMaterials, &pMesh));
	DWORD dError = ::GetLastError();

	/* What is the flexible vertex format of the vertex buffer before cloning? */
	DWORD dMeshFVF = pMesh->GetFVF();

	/* Clone the mesh, ensuring that it has room for position, normals, and texture coordinates */
	ID3DXMesh* pClone = NULL;
	HR(pMesh->CloneMeshFVF(D3DPOOL_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX2, m_pD3DDevice, &pClone));
	pMesh->Release();
	pMesh = pClone;

	/* Recompute the normals if they were not in the file */
	if (0 == (dMeshFVF & D3DFVF_NORMAL))
	{
		HR(D3DXComputeNormals(pMesh, (DWORD*)pAdjacency->GetBufferPointer()));
	}

	/* Optimize the mesh */
	HR(pMesh->OptimizeInplace(D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_COMPACT | D3DXMESHOPT_VERTEXCACHE,
		(DWORD*)pAdjacency->GetBufferPointer(),
		0, 0, 0));

	/* Done with buffers */
	pAdjacency->Release();
	pEffectsInstances->Release();
	pMaterials->Release();
}
ID3DXMesh* Graphics::getMeshByIndex(unsigned index)
{
	return m_pMeshLibrary[index];
}
LPDIRECT3DTEXTURE9 Graphics::getTextureByIndex(unsigned index)
{
	return m_tpTexLibrary[index].lpTexture;
}
void Graphics::RenderFont(D3DXVECTOR3 playerPos)
{
	//Print Upper Left Camera Instructions
	m_Printer.str("");m_Printer.clear();	//Clear Printer
	m_Printer << "::Debug Camera Controls::\n";
	m_Printer << "     W: Zoom In\n";
	m_Printer << "     A: Pitch Up\n";
	m_Printer << "     S: Zoom Out\n";
	m_Printer << "     D: Pitch Down\n";
	m_Printer << "     C: Coordinates\n";

	m_pD3DFont->DrawTextA
		(NULL,m_Printer.str().c_str(),
		m_Printer.str().size(),&m_rLeftInst,
		DT_LEFT | DT_NOCLIP,D3DCOLOR_ARGB(255, 255, 255, 255));

	//Upper Right Player Instructions
	m_Printer.str("");m_Printer.clear();	//Clear Printer
	m_Printer << "::Debug Player Controls::\n";
	m_Printer << "      L:	Left\n";
	m_Printer << "      R: Right\n";
	m_Printer << "      U:	Away\n";
	m_Printer << "      D:	Towards\n";
	m_Printer << "      P:	Coordinates\n";

	m_pD3DFont->DrawTextA
		(NULL,m_Printer.str().c_str(),
		m_Printer.str().size(),&m_rRighInst,
		DT_LEFT | DT_NOCLIP,D3DCOLOR_ARGB(255, 255, 255, 255));

	//Bottom Left Camera Coordinates
	m_Printer.str("");	m_Printer.clear();					
	if(DirectInput::Engine()->keyDown(DIK_C))
	{
		m_Printer << ":: Camera Position ::\n";
		m_Printer << "   x: " << m_Camera.eyePos.x << "\n";
		m_Printer << "   y: " << m_Camera.eyePos.y << "\n";
		m_Printer << "   z: " << m_Camera.eyePos.z << "\n";
	}

	m_pD3DFont->DrawTextA
		(NULL,m_Printer.str().c_str(),
		m_Printer.str().size(),&m_rCameraCrd,
		DT_LEFT | DT_NOCLIP,D3DCOLOR_ARGB(255, 255, 255, 255));

	//Bottom Right Player Coordinates
	m_Printer.str("");	m_Printer.clear();	
	if(DirectInput::Engine()->keyDown(DIK_P))
	{
		m_Printer << ":: Player Position ::\n";
		m_Printer << "   x: " << playerPos.x << "\n";
		m_Printer << "   y: " << playerPos.y << "\n";
		m_Printer << "   z: " << playerPos.z << "\n";

	}

	m_pD3DFont->DrawTextA
		(NULL,m_Printer.str().c_str(),
		m_Printer.str().size(),&m_rPlayerCrd,
		DT_LEFT | DT_NOCLIP,D3DCOLOR_ARGB(255, 255, 255, 255));

	m_Printer.str("");	m_Printer.clear();	
	m_Printer << ":: Credits :: \n";
	m_Printer << "Brandon Rockwell\n";
	m_Printer << "Michael Alstad\n";
	m_Printer << "Brandon York\n";
	m_Printer << "Tyler Lawton\n";

	m_pD3DFont->DrawTextA
		(NULL,m_Printer.str().c_str(),
		m_Printer.str().size(),&m_rCreditCrd,
		DT_LEFT | DT_NOCLIP,D3DCOLOR_ARGB(255, 255, 255, 255));


}
Graphics::~Graphics()
{
	m_pD3DFont->Release();

	if (NULL != m_pD3DDevice)
	{

		m_pD3DDevice->Release(); 
		// it may be important later on for the
		// device's release function to be called last
	}

}